using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;

namespace Agile.Common
{
    /// <summary>
    /// Summary description for Files.
    /// </summary>
    public class Files
    {

                /// <summary>
        /// Searches for the text to be replaced in the file contents.
        /// If found it gets replaced with the new text.
        /// </summary>
        /// <param name="file">file to search and replace the string in.</param>
        /// <param name="replaceThisText">Text that is to be replaced.</param>
        /// <param name="withThisText">The new text.</param>
        /// <param name="forceWriteOverReadOnlyFile"></param>
        /// <returns>True if the text was successfully replaced.</returns>
        public static bool ReplaceStringIn(FileInfo file
                                           , string replaceThisText
                                           , string withThisText
            , bool forceWriteOverReadOnlyFile)
        {
            // if it is read only, make it writable if we are 'forcing' the write
            if (file.IsReadOnly)
            {
                if (forceWriteOverReadOnlyFile)
                    file.IsReadOnly = false;
                else
                    return false;
            }

            string contents = File.ReadAllText(file.FullName);
            if (Strings.Contains(contents, replaceThisText))
            {
                try
                {
                    // Write the modification into the same file
                    StreamWriter streamWriter = File.CreateText(file.FullName);
                    streamWriter.Write(contents.Replace(replaceThisText, withThisText));
                    streamWriter.Close();
                    return true;
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }

            }
            return false;
        }

        /// <summary>
        /// Searches for the text to be replaced in the file contents.
        /// If found it gets replaced with the new text.
        /// </summary>
        /// <param name="file">file to search and replace the string in.</param>
        /// <param name="replaceThisText">Text that is to be replaced.</param>
        /// <param name="withThisText">The new text.</param>
        /// <returns>True if the text was successfully replaced.</returns>
        public static bool ReplaceStringIn(FileInfo file
                                           , string replaceThisText
                                           , string withThisText)
        {
            return ReplaceStringIn(file, replaceThisText, withThisText, false);
        }


        /// <summary>
        /// Returns true if the file contents contains the given text.
        /// </summary>
        /// <param name="file">Check this file.</param>
        /// <param name="textToLookFor">The text to look for in the file.</param>
        /// <returns>True if the file contains the text.</returns>
        public static bool FileContainsText(FileInfo file
                                            , string textToLookFor)
        {
            string fileContents = File.ReadAllText(file.FullName);
            return Strings.Contains(fileContents, textToLookFor);
        }

        /// <summary>
        /// Checks if the file is the right file type, ie has the right file extension.
        /// The extension and the desired file type must be an exact match.
        /// </summary>
        /// <param name="file">Check the file type of this is the type we want.</param>
        /// <param name="desiredFileTypes">The file extension(s) you want the file to be (do not include the "."!)</param>
        /// <returns></returns>
        public static bool IsTheRightFileType(FileInfo file
                                              , params string[] desiredFileTypes)
        {
            foreach (string type in desiredFileTypes)
            {
                if(file.Extension.Replace(".", string.Empty) == type)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Checks if the file is the right file type, by checking if the files extension
        /// ENDS WITH the given text.
        /// </summary>
        /// <param name="file">Check the file type of this is the type we want.</param>
        /// <param name="desiredFileType">The file extension you want the file to be (do not include the "."!)</param>
        /// <returns></returns>
        public static bool IsTheRightFileTypeSoftMatch(FileInfo file
                                                       , string desiredFileType)
        {
            return file.Extension.Replace(".", string.Empty).EndsWith(desiredFileType);
        }

        /// <summary>
        /// Delegate for performing an action on a directory.
        /// </summary>
        public delegate void PerformActionOnDirectory(DirectoryInfo directory);

        /// <summary>
        /// Delegate for performing an asynchronous action on a directory.
        /// </summary>
        public delegate void PerformActionOnDirectoryAsync(Object directory, AsyncCallback callback);

        /// <summary>
        /// Loop through all of the sub directories of the given directory.
        /// </summary>
        /// <param name="directory">'Parent' directory. Action is performed on THIS directory and all sub directories.</param>
        /// <param name="action">The action to perform on the directory and its sub directories.</param>
        /// <param name="ignoreDirectoriesStartingWith">Ignore any directory (and its sub directories) that starts with any of the
        /// given strings. Use only for directories like '_svn' or where you know the name is unique.</param>
        public static void LoopThroughSubDirectories(DirectoryInfo directory
                                                     , PerformActionOnDirectory action
                                                    , params string[] ignoreDirectoriesStartingWith)
        {
            if (ignoreDirectoriesStartingWith != null)
            {
                foreach (string ignore in ignoreDirectoriesStartingWith)
                {
                    if (directory.Name.StartsWith(ignore))
                        return;
                }
            }

            foreach (DirectoryInfo sub in directory.GetDirectories())
            {
                //if (sub.GetDirectories().Length > 0)
                LoopThroughSubDirectories(sub, action, ignoreDirectoriesStartingWith);
            }

            // After looping through all of the sub directories, do the action
            action(directory);
        }

        /// <summary>
        /// Loop through all of the sub directories of the given directory.
        /// </summary>
        /// <param name="callback"></param>
        /// <param name="directory">'Parent' directory. Action is performed on THIS directory and all sub directories.</param>
        /// <param name="asyncAction">The action to perform on the directory and its sub directories.</param>
        public static void LoopThroughSubDirectoriesAsync(DirectoryInfo directory
            , AsyncCallback callback
            , PerformActionOnDirectoryAsync asyncAction)
        {
            foreach (DirectoryInfo sub in directory.GetDirectories())
            {
                //if (sub.GetDirectories().Length > 0)
                LoopThroughSubDirectoriesAsync(sub, callback, asyncAction);
            }
            // After looping through all of the sub directories, do the action
            asyncAction(directory, callback);
        }



        /// <summary>
        /// Delegate for performing an action on a file.
        /// </summary>
        public delegate void PerformActionOnFile(FileInfo file);

        /// <summary>
        /// Delegate for performing an asynchronous action on a file.
        /// </summary>
        public delegate void PerformActionOnFileAsync(Object file, AsyncCallback callback);

        /// <summary>
        /// Loop through all of the files in the directory.
        /// </summary>
        /// <param name="directory">Directory to search through files in.</param>
        /// <param name="action">The action to perform on the file.</param>
        public static void GoThroughFilesIn(DirectoryInfo directory
                                            , PerformActionOnFile action)
        {
            foreach (FileInfo file in directory.GetFiles())
            {
                Application.DoEvents();
                action(file);
            }
        }


        /// <summary>
        /// Clean outputs from the given directory.
        /// Deletes all 'bin' and 'obj' directories in the given directory
        /// AND its sub directories.
        /// </summary>
        /// <param name="directory">Delete all bin and obj directories in this directory.</param>
        public static void CleanOutputsIn(DirectoryInfo directory)
        {
            // Code that does 'the work' has been encapsulated in its own class...
            CleanOutputsIn(directory, null);
        }
        
        /// <summary>
        /// Clean outputs from the given directory.
        /// Deletes all 'bin' and 'obj' directories in the given directory
        /// AND its sub directories.
        /// </summary>
        /// <param name="directory">Delete all bin and obj directories in this directory.</param>
        /// <param name="observer">Observer to notify of changes.</param>
        public static void CleanOutputsIn(DirectoryInfo directory, IObserver observer)
        {
            // Code that does 'the work' has been encapsulated in its own class...
            CleanDevelopmentDirectories.CleanOutputsAsync(directory, observer);
        }
    }
}